﻿//Srinivasan

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Forms;
using System.Text.RegularExpressions;

namespace Tzync
{
    /*********************************************************************************************************/
    /* Class to Analyze the Host System's files and its MetaData and find Conflicting/Non-Conflicting Updates */
    /*********************************************************************************************************/

    partial class Analyze
    {
        private bool firstSyncGuest;
        private List<TreeNode> arrNode;
        private List<Difference> differences;
        private SystemType systemName;
        private TreeOperation treeHandler;
		private ISubject differenceSubject;
        private String storeFilesDir, profilePath;
        private Filter filter;
        
        //Description     : Constructor to initialise Analyze.arrNode Tree and systemName and secondTime

        public Analyze(List<TreeNode> arrNodes, bool sT, SystemType st, ISubject subject, ref Filter f, String profileHDPath)
        {
            firstSyncGuest = sT;
            arrNode = arrNodes;            
            systemName = st;
            differences = new List<Difference>();
            treeHandler = new TreeOperation(arrNode, sT, st);
			differenceSubject = (DifferenceSubject)subject;
            filter = f;            
            profilePath = Path.Combine(profileHDPath, "Files");
            storeFilesDir = profilePath;            
        }

        //Description     : This function is a wrapper for findDiff() function. 
        //Pre-Conditions  : D is not NULL
        //Post-Conditions : List<Difference> differences is computed.

        public List<Difference> GetDifferences(DirectoryInfo D)
        {
            try
            {
                FindDiff(D, arrNode[0]);
            }            
            catch (System.ArgumentNullException)
            {                
                throw new ArgumentNullException(D.FullName, ActionType.Analyze);
            }
            catch (System.ArgumentOutOfRangeException)
            {
                throw new ArgumentOutOfRangeException(ActionType.Analyze);
            }
            
            return differences;
        }

        //Description     : Given a system Directory, a corresponding log node, this function computes the differences
        // between the two file system trees.         

        public void FindDiff(DirectoryInfo D, TreeNode N) //throw Exception
        {            
            if (D == null) throw new ArgumentNullException(D.FullName, ActionType.Analyze);
            if (N == null)
            {
                if (systemName == SystemType.Host)
                    throw new ArgumentNullException(N.GetFullPathHost(), ActionType.Analyze);
                else
                    throw new ArgumentNullException(N.GetFullPathGuest(), ActionType.Analyze);
            }                        

            if (N.GetId() == 0)
            {
                arrNode[0] = treeHandler.UpdateRootNode(D, N);                
            }          

            FileInfo[] sysFiles;
            List<int> fileChilds, folderChilds;
            DirectoryInfo[] sysDirectories;

            //Get differences in sub-files of this folder
            sysFiles = D.GetFiles();
            fileChilds = treeHandler.getFileChilds(N);                    
            FindFileDifference(sysFiles, fileChilds, N.GetId());            

            //Get differences in sub-folders of this folder  
            sysDirectories = D.GetDirectories();            
            folderChilds = treeHandler.getFolderChilds(N);                      
            FindFolderDifference(sysDirectories, folderChilds, N.GetId());   //This call recursively calls this method.                                     
        }

        // Description     : Given a list of files and corresponding log entry TreeNodes, this function simply
        // computes the differences between them and adds to the main list of differences.                         

        private void FindFileDifference(FileInfo[] sysFiles, List<int> fileChilds, int nodeParent)
        {            
            Pair<DirectionType, OperationType> fileStatus = new Pair<DirectionType, OperationType>();
            int sysSize = sysFiles.Count(), partnerSize = fileChilds.Count(), partnerIndex;
            List<String> nodeFileNames = new List<string>();
            bool[] partnerFileVisited = new bool[fileChilds.Count()];            
            int vA_f, vA_s, vB_f, vB_s;
            
            for (int i = 0; i < partnerSize; i++)
            {
                partnerFileVisited[i] = false; 
                nodeFileNames.Add(arrNode[fileChilds[i]].GetName());                
            }

            int counter = 1;
            for (int i = 0; i < sysSize; i++)
            {                
                partnerIndex = nodeFileNames.IndexOf(sysFiles[i].Name);
                storeFilesDir = profilePath;
                storeFilesDir = Path.Combine(storeFilesDir, Utility.StripString(sysFiles[i].FullName));                                

                if (partnerIndex >= 0 && fileChilds[partnerIndex] < arrNode.Count())
                {                    
                    vA_f = arrNode[fileChilds[partnerIndex]].GetVersionHost().First;
                    vA_s = arrNode[fileChilds[partnerIndex]].GetVersionHost().Second;
                    vB_f = arrNode[fileChilds[partnerIndex]].GetVersionGuest().First;
                    vB_s = arrNode[fileChilds[partnerIndex]].GetVersionGuest().Second;
                    partnerFileVisited[partnerIndex] = true;                    
                    
                    //If file is found in Current system, the following region checks for appropriate set of differences

                    #region CheckFileDifferencesRegion1
                    
                    //If current system is Host, Check for differences
                    if (systemName == SystemType.Host)
                    {
                        arrNode[fileChilds[partnerIndex]].SetFullPathHost(sysFiles[i].FullName);
                        Analyze_File(sysFiles[i], fileChilds, partnerIndex, vA_f, vA_s, vB_f, vB_s, arrNode[fileChilds[partnerIndex]].GetSyncHost());
                    }

                    //If current system is Guest, Check for differences

                    else if (systemName == SystemType.Guest)
                    {
                        arrNode[fileChilds[partnerIndex]].SetFullPathGuest(sysFiles[i].FullName);
                        fileStatus = new Pair<DirectionType, OperationType>();

                        //If this is the first time in System B
                        if (firstSyncGuest == true)
                        {                            
                            String dT = Utility.ParseDateTime(sysFiles[i].LastWriteTime);

                            if (dT.CompareTo(arrNode[fileChilds[partnerIndex]].GetSyncHost()) != 0)
                            {                               
                                fileStatus.First = DirectionType.None;
                                fileStatus.Second = OperationType.ModModConflict;
                                AddDifference(new Difference(sysFiles[i], arrNode[fileChilds[partnerIndex]], fileStatus));
                            }
                            else
                            {
                                arrNode[fileChilds[partnerIndex]].GetVersionGuest().First = arrNode[fileChilds[partnerIndex]].GetVersionHost().First;
                                arrNode[fileChilds[partnerIndex]].GetVersionGuest().Second = arrNode[fileChilds[partnerIndex]].GetVersionHost().Second;
                            }                            
                            continue;
                        }                        
                        Analyze_File(sysFiles[i], fileChilds, partnerIndex, vB_s, vB_f, vA_s, vA_f, arrNode[fileChilds[partnerIndex]].GetSyncGuest());
                    }
                    #endregion
                }                

                //Non-Conflicting Creation in Current system

                if (partnerIndex < 0)
                {                    
                    TreeNode N;                                                                                                           

                    if (filter.IsValidFile(sysFiles[i].Name, sysFiles[i].FullName))
                    {                        
                        N = treeHandler.fileUpdateArrNode(sysFiles[i], nodeParent); 
                        fileStatus.First = DirectionType.SystemToHD; fileStatus.Second = OperationType.Creation;
                        AddDifference(new Difference(sysFiles[i], N, fileStatus));                        
                    }                                                                              
                }                
                counter++;
            }

            //If file is not found in Current System, but a TreeNode for the file exists
            //Check for (Deletions in Current System)/(Creations in Partner System)

            for (int i = 0; i < partnerSize; i++)
            {
                if (fileChilds[i] >= arrNode.Count()) continue;

                vA_f = arrNode[fileChilds[i]].GetVersionHost().First;
                vA_s = arrNode[fileChilds[i]].GetVersionHost().Second;
                vB_f = arrNode[fileChilds[i]].GetVersionGuest().First;
                vB_s = arrNode[fileChilds[i]].GetVersionGuest().Second;

                //If the file is not found in Current system, the following function checks for appropriate set of differences 
                               
                if (partnerFileVisited[i] == false && fileChilds[i] != -1 && systemName==SystemType.Host)
                {
                    storeFilesDir = profilePath;
                    storeFilesDir = Path.Combine(storeFilesDir, Utility.StripString(arrNode[fileChilds[i]].GetFullPathGuest()));

                    Analyze_FileTreeNode(fileChilds, nodeParent, vA_f, vA_s, vB_f, vB_s, i, arrNode[fileChilds[i]].GetFullPathHost());
                }

                else if (partnerFileVisited[i] == false && fileChilds[i] != -1 && systemName == SystemType.Guest)
                {
                    storeFilesDir = profilePath;
                    storeFilesDir = Path.Combine(storeFilesDir, Utility.StripString(arrNode[fileChilds[i]].GetFullPathHost()));

                    Analyze_FileTreeNode(fileChilds, nodeParent, vB_s, vB_f, vA_s, vA_f, i, arrNode[fileChilds[i]].GetFullPathGuest());
                }                
            }       

        }

        //Function to analyze a single file and get the appropriate difference, if any.

        private void Analyze_File(FileInfo sysFile, List<int> fileChilds, int partnerIndex, int SystemSystem, int SystemPartner, int PartnerSystem, int PartnerPartner, String storedTime)
        {            
            String dT = Utility.ParseDateTime(sysFile.LastWriteTime);

            Pair<DirectionType, OperationType> fileStatus = new Pair<DirectionType, OperationType>();

            //If file in Current System is dirty

            if (dT.CompareTo(storedTime) != 0)
            {
                //If the file in Partner System is also dirty -- its conflicting update

                if (SystemPartner != PartnerPartner)
                {
                    fileStatus.First = DirectionType.None;

                    //Modification in Current System - Deletion in Partner System Conflict

                    if (PartnerSystem == 0 && PartnerPartner == -1)
                        fileStatus.Second = OperationType.ModDeleteConflict;

                    //Detecting Creation-Creation(First Detected in Partner System) Conflict

                    else if (PartnerSystem == 0 && PartnerPartner == 1 && SystemSystem == 0 && SystemPartner == 0)
                        fileStatus.Second = OperationType.CreationCreationConflict;

                    //Detecting Mod-Mod Conflict

                    else fileStatus.Second = OperationType.ModModConflict;

                    AddDifference(new Difference(sysFile, arrNode[fileChilds[partnerIndex]], fileStatus));
                }

                //If the file is modified in Current System only -- Non-Conflicting Update from Current System

                else if (filter.IsValidFile(sysFile.Name, sysFile.FullName))
                {
                    fileStatus.First = DirectionType.SystemToHD;
                    fileStatus.Second = OperationType.Modification;
                    AddDifference(new Difference(sysFile, arrNode[fileChilds[partnerIndex]], fileStatus));
                }

            }

            //Else if only Partner System file might be dirty 

            else
            {
                //Check if partner File is dirty! -- Non-Conflicting update from Partner Side

                if (SystemPartner != PartnerPartner)
                {
                    fileStatus.First = DirectionType.HDToSystem;
                    if (PartnerPartner != -1)
                    {
                        //If a conflict was resolved in the previous sync in current computer and not yet taken to the
                        // the partner computer                                    
                        if (!File.Exists(storeFilesDir))
                        {
                            fileStatus.Second = OperationType.Modification;
                            AddDifference(new Difference(sysFile, arrNode[fileChilds[partnerIndex]], fileStatus));
                        }
                    }

                    //If the file is deleted in the partner side

                    else
                    {
                        //If a conflict was resolved in the previous sync in this computer and not yet taken to the
                        // the other computer                                    
                        if (!File.Exists(storeFilesDir))
                        {
                            fileStatus.Second = OperationType.Deletion;
                            AddDifference(new Difference(sysFile, arrNode[fileChilds[partnerIndex]], fileStatus));
                        }
                    }
                }

            }
        }

        //Analyzes a single TreeNode for a file not in current system and gets the appropriate difference 
        
        private void Analyze_FileTreeNode(List<int> fileChilds, int nodeParent, int SystemSystem, int SystemPartner, int PartnerSystem, int PartnerPartner, int i, String fileAddress)
        {
            Pair<DirectionType, OperationType> f = new Pair<DirectionType, OperationType>();

            //Non-Conflicting Creation in the Partner System

            if (SystemSystem == 0 && SystemPartner == 0)
            {                
                String newAddress;
                f.First = DirectionType.HDToSystem; f.Second = OperationType.Creation;                
                if (systemName == SystemType.Host)
                {
                    newAddress = Path.Combine(arrNode[nodeParent].GetFullPathHost(), arrNode[fileChilds[i]].GetName());
                    arrNode[fileChilds[i]].SetFullPathHost(newAddress);
                    AddDifference(new Difference(new FileInfo(@arrNode[fileChilds[i]].GetFullPathGuest()), arrNode[fileChilds[i]], f));
                }
                else if (systemName == SystemType.Guest)
                {
                    newAddress = Path.Combine(arrNode[nodeParent].GetFullPathGuest(), arrNode[fileChilds[i]].GetName());
                    arrNode[fileChilds[i]].SetFullPathGuest(newAddress);
                    AddDifference(new Difference(new FileInfo(@arrNode[fileChilds[i]].GetFullPathHost()), arrNode[fileChilds[i]], f));
                }                
            }

            //Conflicting (Deletion in Current System)/(Update in the partner System)

            else if (SystemPartner != PartnerPartner)
            {
                f.First = DirectionType.None;

                //If Deletion was first detected in Partner System, then its DeletionDeletionConflict (Handled automatically)

                if (PartnerSystem == 0 && PartnerPartner == -1)
                {
                    f.First = DirectionType.HDToSystem;
                    f.Second = OperationType.DeletionDeletionConflict;
                    AddDifference(new Difference(new FileInfo(@fileAddress), arrNode[fileChilds[i]], f));                    
                }

                //If Partner System just modified and Current System deleted

                else
                {
                    if (SystemSystem == -1 && SystemPartner == 0 && PartnerSystem != -1 && PartnerPartner != -1)
                    {
                        //To check if a conflict was resolved in a previous sync in Current System and not yet taken to the
                        // the other computer, else proceed

                        if (File.Exists(storeFilesDir))
                        {                            
                            f.First = DirectionType.HDToSystem;
                            f.Second = OperationType.Modification;
                            AddDifference(new Difference(new FileInfo(@fileAddress), arrNode[fileChilds[i]], f));
                        }

                    }
                    else
                    {
                        f.Second = OperationType.ModDeleteConflict;
                        AddDifference(new Difference(new FileInfo(@fileAddress), arrNode[fileChilds[i]], f));
                    }
                }
            }

            //Non-Conflicting Deletion in Current System

            else
            {   
                //Check if Deletion was detected here first not yet taken to other system

                if (!(SystemSystem==-1 && SystemPartner==0) && filter.IsValidFile(arrNode[fileChilds[i]].GetName(), fileAddress))
                {                    
                    f.Second = OperationType.Deletion;
                    f.First = DirectionType.SystemToHD;
                    AddDifference(new Difference(new FileInfo(@fileAddress), arrNode[fileChilds[i]], f));
                }
            }            
        }                

        //Description     :  Function to add a difference to the list of Differences maintained in the class.

        private void AddDifference(Difference d)
        {
            if (d == null) return;
            differences.Add(d);
			differenceSubject.SetDifference(d);  //notify UI to display newly found conflicts
        }
        
    }

    /**********************/
    /* Class Analyze Ends */
    /**********************/
}
